package com.opennews.openplatform.service;

import com.opennews.openplatform.myspringbootcore.util.DateHelper;
import com.opennews.openplatform.myspringbootcore.util.SharedUtil;
import lombok.RequiredArgsConstructor;
import org.jooq.DSLContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import static com.opennews.openplatform.entity.Tables.USER;

@RequiredArgsConstructor
@Service
public class FileService {
    private final DSLContext dslContext;
    private final Environment env;
    private final UserService userService;

    /**
     * Uploads the avatar by using the file content in base64 format.
     *
     * @param userId:           The string of the user'id.
     * @param originalFilename: The string of original filename of avatar.
     * @param base64Content:    The file content in base64 format.
     * @return The string of new avatar full url.
     */
    public String uploadAvatarByBase64(String userId, String originalFilename, String base64Content) {
        // Generates the new filename with timestamp.
        String filename = generateFilename(originalFilename);

        // Decodes the input base64 content.
        byte[] bytes = Base64.getDecoder().decode(base64Content);

        // Gets the avatar file saved path.
        File uploadAvatarPath = getUploadedAvatarPath();

        // The file instance of new avatar.
        File uploadAvatarFile = new File(uploadAvatarPath, filename);

        // Writes the bytes to new avatar file.
        try (FileOutputStream stream = new FileOutputStream(uploadAvatarFile)) {
            stream.write(bytes);
            stream.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        // Updates user avatar in the data source and removes old avatar file.
        updateUserAvatar(userId, uploadAvatarPath, filename);

        // Returns the filename as output.
        // The filename type is GString. That's why toString used here.
        return userService.getAvatarUrl(filename);
    }

    /**
     * Uploads the avatar by using the uploaded file.
     *
     * @param userId: The string of the user'id.
     * @param file:   The uploaded file of avatar.
     * @return The string of new avatar full url.
     */
    public String uploadAvatar(String userId, MultipartFile file) throws IOException {
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        // Gets the file original file name.
        String originalFilename = file.getOriginalFilename();

        // Generates the new filename with timestamp.
        String filename = generateFilename(originalFilename);

        // Checks if the file is empty.
        if (!file.isEmpty()) {
            // Gets the avatar file saved path.
            File uploadAvatarPath = getUploadedAvatarPath();

            // The file instance of new avatar.
            File uploadAvatarFile = new File(uploadAvatarPath, filename);

            // Transfers the file to the path.
            file.transferTo(uploadAvatarFile);

            // Updates user avatar in the data source and removes old avatar file.
            updateUserAvatar(userId, uploadAvatarPath, filename);

            // Returns the filename as output.
            // The filename type is GString. That's why toString used here.
            return userService.getAvatarUrl(filename);
        } else {
            return "";
        }
    }

    /**
     * Uploads file with scaling width.
     *
     * @param file:          The uploaded file.
     * @param directoryName: The name of the directory where the file is uploaded to.
     * @param targetWidth:   The int of the target image width. If scaling is needed please specify this value.
     * @return The string of new upload filename.
     */
    public String uploadFile(MultipartFile file, String directoryName, int targetWidth) throws IOException {
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        // Gets the file original file name.
        String originalFilename = file.getOriginalFilename();

        // Generates the new filename with timestamp.
        String filename = generateFilename(originalFilename);

        // Checks if the file is empty.
        if (!file.isEmpty()) {
            // Gets the upload file saved path.
            File uploadPath = getUploadedDirectoryPath(directoryName);

            // The file instance of new upload.
            File uploadFile = new File(uploadPath, filename);

            // Transfers the file to the path.
            file.transferTo(uploadFile);

            // Returns the filename as output.
            return filename;
        } else {
            return "";
        }
    }

    /**
     * Uploads file without scaling.
     *
     * @param file:          The uploaded file.
     * @param directoryName: The name of the directory where the file is uploaded to.
     * @return The string of new upload filename.
     */
    public String uploadFile(MultipartFile file, String directoryName) throws IOException {
        return uploadFile(file, directoryName, -1);
    }

    /**
     * For demo purpose only at this moment.
     *
     * @param files
     * @return
     */
    public List<String> uploadFiles(List<MultipartFile> files) throws IOException {
        List<String> newFilenames = new ArrayList<>();

        // def d = request.multipartFiles
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        for (MultipartFile file : files) {
            // Gets the file original file name.
            String filename = file.getOriginalFilename();

            // Gets the last position of '.'. This is for getting the file extension.
            int lastDotIndex = filename.lastIndexOf(".");

            // If the filename contains extension then we need to keep it.
            if (lastDotIndex > -1) {
                filename = DateHelper.format(DateHelper.getCurrentLocalDateTime(), DateHelper.LONG_TIMESTAMP) + "-" + filename.substring(lastDotIndex + 1);
            } else {
                filename = DateHelper.format(DateHelper.getCurrentLocalDateTime(), DateHelper.LONG_TIMESTAMP) + "-" + SharedUtil.nextRandomIntInRange(100000, 999);
            }

            // Checks if the file is empty.
            if (!file.isEmpty()) {
                File tempFile = new File("D:/" + filename);
                file.transferTo(tempFile);

                // Saves the temp file path.
                newFilenames.add(tempFile.getPath());
            }
        }

        return newFilenames;
    }

    public void delete(String filename) {
        File file = new File(getUploadedAvatarPath(), filename);

        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * Generates the new avatar filename based on original file extension name.
     *
     * @param originalFilename: The string of uploaded file original filename.
     * @return The string of new generated filename with original file extension name.
     */
    private String generateFilename(String originalFilename) {
        // Generates the new filename with timestamp.
        String filename = String.format("%s-%s", DateHelper.format(DateHelper.getCurrentLocalDateTime(), DateHelper.LONG_TIMESTAMP),
                SharedUtil.nextRandomIntInRange(100000, 999999));

        // Gets file extension.
        String extension = getFileExtension(originalFilename);

        // If the filename contains extension then we need to keep it.
        if (!SharedUtil.isNullOrEmpty(extension)) {
            filename += "." + extension;
        }

        return filename;
    }

    /**
     * Gets extension from filename.
     *
     * @param filename: The string of input filename.
     * @return The string of file extension. Example: "jpg". Please notice the leading dot is NOT included.
     */
    private String getFileExtension(String filename) {
        // Gets the last position of '.'. This is for getting the file extension.
        int lastDotIndex = filename.lastIndexOf(".");

        // If the filename contains extension.
        if (lastDotIndex > -1) {
            return filename.substring(lastDotIndex + 1);
        }

        return "";
    }

    /**
     * Gets file instance of uploaded avatar path.
     *
     * @return The file instance of the new avatar path which avatar files saved to.
     */
    private File getUploadedAvatarPath() {
        return getUploadedDirectoryPath(env.getProperty("resource.avatarDirectoryName"));
    }

    /**
     * Gets file instance of uploaded directory path.
     *
     * @return The file instance of the new directory path which uploaded files saved to.
     */
    private File getUploadedDirectoryPath(String directoryName) {
        // Gets the upload avatar file path.
        File uploadPath = new File(env.getProperty("resource.upload.rootFilePath"), directoryName);

        // If the path does not exist then creates it.
        if (!uploadPath.exists()) {
            uploadPath.mkdirs();
        }

        return uploadPath;
    }

    /**
     * Updates user's avatar filename and deletes old avatar file.
     *
     * @param userId:           The string of the user id.
     * @param uploadAvatarPath: The file instance of the path where avatar files saved to.
     * @param filename:         The new avatar filename.
     */
    private void updateUserAvatar(String userId, File uploadAvatarPath, String filename) {
        // Gets the user for deleting old avatar file and updating new one.
        String currentAvatarFilename = dslContext.selectFrom(USER)
                .where(USER.ID.eq(userId))
                .fetchOne(USER.AVATAR_FILENAME);

        // Deletes the old avatar file if needed.
        if (!SharedUtil.isNullOrEmpty(currentAvatarFilename)) {
            File currentAvatarFile = new File(uploadAvatarPath, currentAvatarFilename);

            if (currentAvatarFile.exists()) {
                currentAvatarFile.delete();
            }
        }

        // Saves the new avatar filename.
        dslContext.update(USER)
                .set(USER.AVATAR_FILENAME, filename)
                .set(USER.LAST_UPDATED, DateHelper.getCurrentLocalDateTime())
                .where(USER.ID.eq(userId))
                .execute();
    }
}
